package fr.bludwarf.skywarrior.evenements;

import static fr.bludwarf.skywarrior.global.Temps.INFINI;

import java.util.Collection;
import java.util.TreeSet;

import fr.bludwarf.skywarrior.exceptions.IleInconnueException;
import fr.bludwarf.skywarrior.global.Etat;
import fr.bludwarf.skywarrior.global.EtatMonde;
import fr.bludwarf.skywarrior.global.TimeFactorInconnu;


public abstract class Evenement implements Comparable<Evenement> {
	
	/** temps restant avant l'évènement */
	// FIXME : remettre en private dès que la méthode avancer a été créée
	protected double _tempsRestant = INFINI;
	
	// TODO : stocker le temps passé sur un évènement en cours

	/**
	 * @param tempsRestant temps restant avant l'évènement
	 */
	public Evenement(final double tempsRestant) {
		_tempsRestant = tempsRestant;
	}
	
	public double tempsRestant()
	{
		return _tempsRestant;
	}

	/**
	 * Execute l'évènement sur l'état d'un monde
	 * @param etat etat sur lequel s'applique l'évènement
	 * @throws IleInconnueException 
	 */
	public abstract void execute(final EtatMonde etatMonde) throws Exception;
	
	/**
	 * @param evenements liste d'évènements
	 * @return l'évènement qui arrivera le plus tôt parmis toute une liste d'évènements
	 */
	public static Evenement prochainEvenementParmis(final Evenement ... evenements)
	{
		Evenement prochainEvenement = null;
		
		for (final Evenement evenement : evenements)
		{
			if (prochainEvenement == null || evenement != null && evenement.compareTo(prochainEvenement) < 0)
			{
				if (evenement != null && evenement._tempsRestant != INFINI && !Double.isInfinite(evenement._tempsRestant))
				{
					prochainEvenement = evenement;
				}
			}
		}
		
		return prochainEvenement;
	}
	
	/**
	 * @param evenements liste d'évènements
	 * @return l'évènement qui arrivera le plus tôt parmis toute une liste d'évènements
	 */
	// TODO : fusionner avec prochainEvenementDe(final Collection<E> etats)
	public static Evenement prochainEvenementParmis(final Collection<Evenement> evenements)
	{
		Evenement prochainEvenement = null;
		
		for (final Evenement evenement : evenements)
		{
			if (prochainEvenement == null || evenement != null && evenement.compareTo(prochainEvenement) < 0)
			{
				if (evenement != null && evenement._tempsRestant != INFINI)
				{
					prochainEvenement = evenement;
				}
			}
		}
		
		return prochainEvenement;
	}

	/**
	 * @param <T> type des états
	 * @param etats liste d'états
	 * @return l'évènement qui se produira le plus tôt parmis tous les prochains évènements de tous les états, null si aucun état
	 * @throws Exception 
	 */
	public static <E extends Etat<?>> Evenement prochainEvenementDe(final Collection<E> etats) throws Exception {

		final TreeSet<Evenement> evenements = new TreeSet<Evenement>();
		for (final E etat : etats)
		{
			final Evenement prochainEvenement = etat.prochainEvenement();
			if (prochainEvenement != null)
			{
				evenements.add(prochainEvenement);
			}
		}
		
		if (evenements.isEmpty())
		{
			return null;
		}
		else
		{
			return evenements.first();
		}
	}
	
	public int compareTo(Evenement e) {
		final int thisT = (int) Math.round(this._tempsRestant);
		final int eT = (int) Math.round(e._tempsRestant);
		
		if (thisT == eT)
		{
			if (this._tempsRestant < e._tempsRestant)
			{
				return -1;
			}
			else if (e._tempsRestant < this._tempsRestant)
			{
				return 1;
			}
			else
			{
				return 0;
			}
		}
		return thisT - eT;
	}
	
	/**
	 * @return l'évènement est affiché dans l'interface du jeu ?
	 * @since 16 nov. 2011
	 */
	public boolean estVisible() {
		return true;
	}
	
	public boolean estPossible(EtatMonde etatMonde) throws EvenementImpossible
	{
		return true;
	}
	
	public double getTempsRestant()
	{
		return _tempsRestant;
	}
}
